package com.dataseek.iot.device.mqtt;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dataseek.iot.device.dto.LiquidJarDto;
import com.dataseek.iot.device.entity.*;
import com.dataseek.iot.device.service.*;
import com.dataseek.iot.toolikit.common.util.DateUtils;
import com.dataseek.iot.toolkit.mybatis.entity.DeviceList;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * ClassName:PubCallBack
 * Describe:
 * Author:DGJ
 * Data:2020/10/29 10:48
 */
@Slf4j
@Component
public class PubCallBackSys implements MqttCallback {

    private MqttSubSys mqttSub;

    @Autowired
    private MqttLogService mqttLogService;

    @Autowired
    private LiquidJarService liquidJarService;

    @Autowired
    private DeviceAttValueService deviceAttValueService;

    @Autowired
    private DeviceListService deviceListService;

    @Autowired
    private NotifyRuleService notifyRuleService;

    @Autowired
    private AlarmLogService alarmLogService;

    private static DeviceListService deviceListServiceStatic;
    private static NotifyRuleService notifyRuleServiceStatic;
    private static MqttLogService mqttLogServiceStatic;
    private static LiquidJarService liquidJarServiceStatic;
    private static DeviceAttValueService deviceAttValueServiceStatic;
    private static AlarmLogService alarmLogServiceStatic;


    public PubCallBackSys(MqttSubSys subsribe) throws MqttException {
        this.mqttSub = subsribe;
    }

    @PostConstruct
    public void init() {
        notifyRuleServiceStatic = notifyRuleService;
        mqttLogServiceStatic = mqttLogService;
        liquidJarServiceStatic = liquidJarService;
        deviceAttValueServiceStatic = deviceAttValueService;
        deviceListServiceStatic=deviceListService;
        alarmLogServiceStatic=alarmLogService;
    }


    @Override
    public void connectionLost(Throwable cause) {
        // 连接丢失后，一般在这里面进行重连
        while (true){
            try {
                //如果没有发生异常说明连接成功，如果发生异常，则死循环
                Thread.sleep(1000);
                mqttSub.init();
                break;
            }catch (Exception e){
//                e.printStackTrace();
            }
        }

    }

    @Override
    public void deliveryComplete(IMqttDeliveryToken token) {
        System.out.println("deliveryComplete---------" + token.isComplete());
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {
        // subscribe后得到的消息会执行到这里面
        String result = new String(message.getPayload(),"UTF-8");
        System.out.println("接收消息主题 : " + topic);
        System.out.println("接收消息Qos : " + message.getQos());
        System.out.println("接收消息内容 : " + result);
        //这里可以针对收到的消息做处理
        //解析返回的信息
        String[] topicParams = topic.split("/");
        if(topicParams.length==1){
            log.info(result);
        }
        String deviceId;
        //首先先记录日志
        MqttLog mqttLog=new MqttLog();
        if(topic.contains(MqttConstant.OFFLINE)||topic.contains(MqttConstant.ONLINE)){
            log.error("设备"+ topicParams[0] + message);
            return;
        }

        if(topic.contains(MqttConstant.SYSDESCR)){
            return;
        }
        if(topic.contains(MqttConstant.DISCONNECTED)){//下线的时候更新设备状态
            //ClientId作为deviceCode
            String deviceCode = topicParams[topicParams.length-2].trim();
            long id = System.currentTimeMillis();
            mqttLog.setMqttLogId(id);
            mqttLog.setTopic(topic);
            mqttLog.setMessage(result);
            mqttLog.setMessageId("DISCONNECTED");
            mqttLog.setTimestamp(0L);
            mqttLog.setSendTime(DateUtils.getDateTimeNowStr());
            mqttLogServiceStatic.insertAction(mqttLog);
            //根据code查询
            DeviceList deviceList=deviceListServiceStatic.queryDeviceByCode(deviceCode);
            if(deviceList!=null) {
                deviceList.setStatus("inactive");//设置下线
                deviceListServiceStatic.updateDeviceList(deviceList);
            }
        }else if(topic.contains(MqttConstant.CONNECTED)){
            //ClientId作为deviceCode
            String deviceCode = topicParams[topicParams.length-2].trim();
            long id = System.currentTimeMillis();
            mqttLog.setMqttLogId(id);
            mqttLog.setTopic(topic);
            mqttLog.setMessage(result);
            mqttLog.setMessageId("CONNECTED");
            mqttLog.setTimestamp(0L);
            mqttLog.setSendTime(DateUtils.getDateTimeNowStr());
            mqttLogServiceStatic.insertAction(mqttLog);
            //根据code查询
            DeviceList deviceList=deviceListServiceStatic.queryDeviceByCode(deviceCode);
            if(deviceList!=null) {
                deviceList.setStatus("active");//设置上线
                deviceListServiceStatic.updateDeviceList(deviceList);
            }
        }else {
            //解析各个topic
            deviceId = topicParams[2];
            //解析result
            JSONObject jsonData = JSON.parseObject(result);
            String messageId = jsonData.get("messageId").toString();
            long timestamp = Long.parseLong(jsonData.get("timestamp").toString());
//        String properties=jsonData.get("properties").toString();
//        Map proJson=JSON.parseObject(properties,Map.class);

            //保存发送日志
            mqttLog.setTopic(topic);
            mqttLog.setMessage(result);
            mqttLog.setMessageId(messageId);
            mqttLog.setTimestamp(timestamp);
            mqttLog.setSendTime(DateUtils.getDateTimeNowStr());
            mqttLogServiceStatic.insertAction(mqttLog);

            //查询获取该设备属性
            List<DeviceAttrValue> deviceAttList = deviceAttValueServiceStatic.queryDeviceAttList(deviceId, null);
            if (CollectionUtils.isEmpty(deviceAttList)) {
                log.info("设备{}没有配置属性！", deviceId);
                return;
            }
            if (topic.contains(MqttConstant.REPLY_READ_PROPS)) {//设备属性回复
                String properties = jsonData.get("properties").toString();
                Map proJson = JSON.parseObject(properties, Map.class);
                boolean successFlag = Boolean.parseBoolean(jsonData.get("success").toString());
                if (successFlag) {
                    //更新设备属性
                    for (int i = 0; i < deviceAttList.size(); i++) {
                        DeviceAttrValue deviceAttrValue = deviceAttList.get(i);
                        String attCode = deviceAttrValue.getAttCode();
                        //有对应的属性数据就更新
                        if (proJson.get(attCode) != null) {
                            String attValue = proJson.get(attCode).toString();
                            deviceAttrValue.setAttValue(Float.parseFloat(attValue));
                            deviceAttValueServiceStatic.updateDeviceAtt(deviceAttrValue);
                        }
                    }
                } else {
                    //读取失败
                }
            } else if (topic.contains(MqttConstant.REPORT_PROPS)) {//设备属性上报
                String properties = jsonData.get("properties").toString();
                Map proJson = JSON.parseObject(properties, Map.class);
                DeviceAttrValueHis deviceAttrValueHis = new DeviceAttrValueHis();
                //更新设备属性
                for (int i = 0; i < deviceAttList.size(); i++) {
                    DeviceAttrValue deviceAttrValue = deviceAttList.get(i);
                    String attCode = deviceAttrValue.getAttCode();
                    float oldAttValue = deviceAttrValue.getAttValue();//上一次的属性值
                    //有对应的属性数据就更新
                    AlarmLog alarmLog = new AlarmLog();
                    if (proJson.get(attCode) != null) {
                        String attValue = proJson.get(attCode).toString();
                        deviceAttrValue.setAttValue(Float.parseFloat(attValue));
                        deviceAttValueServiceStatic.updateDeviceAtt(deviceAttrValue);
                        //记录设备液位变化情况
                        System.out.println(oldAttValue + "========new========" + attValue);
                        //原有液位不是0(初始默认为0)并且液位监控大于旧液位，判定可能为加液操作
                        Float current=Float.parseFloat(attValue);
                        if (oldAttValue > 0 && current > oldAttValue) {
                            //还得根据储液罐的值来判断是否加液
                            String deviceCode=deviceAttrValue.getDeviceCode();
                            LiquidJar liquidJar=liquidJarServiceStatic.qryByDeviceCode(deviceCode);
                            if(liquidJar!=null&&liquidJar.getJarThreshold()!=null){
                                String JarThreshold=liquidJar.getJarThreshold();
                                //如果差额大于加液设置的阈值，那就算加液操作
                                if((current-oldAttValue)>=Float.parseFloat(JarThreshold)) {
                                    alarmLog.setAlarmCode("addLiquid");//加液操作code
                                    alarmLog.setAlarmValue(attValue);
                                    alarmLog.setAlarmTime(DateUtils.getDateTimeNowStr());
                                    alarmLog.setAlarmType(5);//5表示加液
                                    alarmLog.setDeviceCode(deviceCode);
                                    alarmLog.setRemark(oldAttValue + "====加液日志===" + attValue);
                                    alarmLogServiceStatic.insertAction(alarmLog);
                                }
                            }

                        }
                        //新增属性历史数据
                        deviceAttrValueHis.setPdtCode(deviceAttrValue.getPdtCode());
                        deviceAttrValueHis.setDeviceCode(deviceAttrValue.getDeviceCode());
                        deviceAttrValueHis.setAttCode(deviceAttrValue.getAttCode());
                        deviceAttrValueHis.setAttValue(Float.parseFloat(attValue));
                        deviceAttrValueHis.setAttName(deviceAttrValue.getAttName());
                        //deviceAttrValueHis.setCreateTime(DateUtils.getDateTimeNowStr());
                        deviceAttValueServiceStatic.insertDeviceAttHis(deviceAttrValueHis);
                    }
                }
            } else if (topic.contains(MqttConstant.EVENT)) {//设备事件上报,告警信息上报/{productId}/{deviceId}/event/{eventId}
                String eventId = topicParams[4];//获取事件id
//            //当前液位值
//            DeviceAttrValue curLevelValue = deviceAttList.stream().filter(d->"liquidLevel".equals(d.getAttCode())).findFirst().orElse(null);
//            if (curLevelValue != null) {
//            }
//            else {
//                log.info("设备{}没有获取到当前液位属性值", deviceId);
//                return;
//            }
                this.sendAlarmMsg(deviceId, result);
            } else if (topic.contains(MqttConstant.REPLY_WRITE_PROPS)) {//调用设备功能修改回复
                String properties = jsonData.get("properties").toString();
                Map proJson = JSON.parseObject(properties, Map.class);
                boolean successFlag = Boolean.parseBoolean(jsonData.get("success").toString());
                if (successFlag) {
                    //需要调用invoke ,将数据保存在设备中，不然设备断电后属性会丢失
                } else {
                    //失败需要再次调用
                }
            }
        }
    }

    //告警通知处理
    private void sendAlarmMsg(String deviceCode, String result) {
        //从消息里获取告警值
        JSONObject dataJson = JSONObject.parseObject(result).getJSONObject("data");
        String type = dataJson.getString("type");
        String value = dataJson.getString("value");
        //报警类型(A：高限报警 B：高高限报警 C：低限报警 D: 低低限报警)
        String alarmType = "";
        int alarmStatus=0;
        switch (type) {
            case "highHigh":
                alarmType = "B";alarmStatus=1;
                break;
            case "high":
                alarmType = "A";alarmStatus=2;
                break;
            case "low":
                alarmType = "C";alarmStatus=3;
                break;
            case "lowLow":
                alarmType = "D";alarmStatus=4;
                break;
            case "recover":
                alarmType = "E";alarmStatus=0;
                break;
            default:
        }

        Map<String, String> paramMap = new HashMap<>();
        paramMap.put("deviceCode", deviceCode);
        paramMap.put("liquidLevel", value);
        paramMap.put("messageId", JSONObject.parseObject(result).getString("messageId"));
        //test,发送高限报警通知,A匹配的是高限报警
        notifyRuleServiceStatic.sendNotifyMsg(alarmType, paramMap);

        //并且更新设备告警状态
       DeviceList deviceList=deviceListServiceStatic.queryDeviceByCode(deviceCode);
        deviceList.setAlarmStatus(alarmStatus);
        deviceListServiceStatic.updateDeviceList(deviceList);

        //记录告警日志情况
        AlarmLog alarmLog=new AlarmLog();
        alarmLog.setAlarmCode(type);
        alarmLog.setAlarmValue(value);
        alarmLog.setAlarmTime(DateUtils.getDateTimeNowStr());
        alarmLog.setAlarmType(alarmStatus);
        alarmLog.setDeviceCode(deviceCode);
        alarmLog.setRemark("告警日志");
        alarmLogServiceStatic.insertAction(alarmLog);
    }
}
